Skip to content

Conversation

@asukaminato0721
Copy link
Contributor

Summary

Fixes #1685

Addressed issue 1685 by making autoimport prefer public shim modules (like collections.abc) over their private implementations and ensuring private entries are deprioritized when a public alternative exists.

Updated to recognize dotted-name private shims (e.g., _collections_abc -> collections.abc) and to sort autoimport candidates so private imports fall behind public ones when both exist.

Test Plan

test autoimport_prefers_public_reexport_for_dotted_private_module

@meta-cla meta-cla bot added the cla signed label Dec 29, 2025
@asukaminato0721 asukaminato0721 marked this pull request as ready for review December 29, 2025 08:11
Copilot AI review requested due to automatic review settings December 29, 2025 08:11
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #1685 by making auto-import suggestions prefer public module re-exports (like collections.abc) over their private implementation modules (like _collections_abc). The fix implements two key changes: (1) extending the should_include_reexport function to recognize dotted-name private shims that encode dots as underscores, and (2) deprioritizing private imports in completion sorting when a public alternative exists.

Key Changes:

  • Enhanced private module shimming detection to handle stdlib patterns like _collections_abccollections.abc
  • Modified autoimport completion logic to track and deprioritize private imports when public alternatives exist
  • Updated sort_text assignment to ensure consistent lexicographic ordering of completion items

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
pyrefly/lib/state/lsp.rs Implements two-pass autoimport candidate collection: first pass collects all candidates and tracks which names have public exports, second pass assigns lower priority sort_text to private imports with public alternatives. Also extends should_include_reexport to handle dotted module name patterns.
pyrefly/lib/test/lsp/completion.rs Adds test validating that public re-exports (foo.bar) are suggested before their private implementations (_foo_bar) in autoimport completions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3199 to +3166
// Some stdlib shims encode dotted modules with underscores (e.g. _collections_abc).
if canonical_module.as_str().starts_with('_') && original_module.as_str().contains('.') {
let canonical_trim = canonical_module.as_str().trim_start_matches('_');
if canonical_trim == original_module.as_str().replace('.', "_") {
return true;
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic for matching dotted-name private shims may produce false positives. It checks if the canonical module starts with '_' and the original contains '.', then compares by replacing dots with underscores. However, this could incorrectly match unrelated modules. For example, _foo_bar would match both foo.bar and foo_bar (a non-dotted module).

Consider adding more stringent checks, such as verifying that the canonical module is a top-level module (no dots in canonical_module), or using a more precise pattern matching approach to avoid false positives.

Copilot uses AI. Check for mistakes.
Comment on lines +1730 to +1845
#[test]
fn autoimport_prefers_public_reexport_for_dotted_private_module() {
let code = r#"
T = Thing
# ^
"#;
let report = get_batched_lsp_operations_report_allow_error(
&[
("main", code),
("_foo_bar", "Thing = 1\n"),
("foo.bar", "from _foo_bar import Thing\n"),
],
get_test_report(Default::default(), ImportFormat::Absolute),
);
assert_eq!(
r#"
# main.py
2 | T = Thing
^
Completion Results:
- (Variable) Thing: from foo.bar import Thing
- (Variable) Thing: from _foo_bar import Thing
# _foo_bar.py
# foo.bar.py
"#
.trim(),
report.trim(),
);
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test validates the general pattern of preferring public re-exports over private modules for dotted module names (e.g., foo.bar vs _foo_bar), but it doesn't test the actual use case mentioned in issue #1685: collections.abc.Iterable vs _collections_abc.Iterable. Consider adding a test case that directly validates the stdlib pattern mentioned in the issue to ensure the fix works for the actual reported problem.

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@yangdanny97
Copy link
Contributor

This might conflict with some in-flight changes from @javabster, so heads up that this might need to be rebased at some point in the next week or so

@github-actions

This comment has been minimized.

@kinto0 kinto0 self-requested a review December 30, 2025 17:50
@kinto0 kinto0 self-assigned this Dec 30, 2025
@kinto0 kinto0 requested review from javabster and removed request for kinto0 December 30, 2025 17:52
@kinto0 kinto0 assigned javabster and unassigned kinto0 Dec 30, 2025
@asukaminato0721 asukaminato0721 force-pushed the 1685 branch 3 times, most recently from 405e955 to caf186f Compare January 13, 2026 19:00
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@kinto0
Copy link
Contributor

kinto0 commented Jan 15, 2026

should this be reviewed (it's currently draft so we've been ignoring it)

@github-actions

This comment has been minimized.

@asukaminato0721
Copy link
Contributor Author

removed depth-based sorting in autoimport completions in lsp.rs, replacing it with a stable sort using label plus private rank to prefer public over private while keeping order consistent.

@asukaminato0721 asukaminato0721 marked this pull request as ready for review January 16, 2026 00:03
@github-actions
Copy link

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@meta-codesync
Copy link

meta-codesync bot commented Jan 16, 2026

@kinto0 has imported this pull request. If you are a Meta employee, you can view this in D90857539.

Copy link
Contributor

@kinto0 kinto0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for working on this! this sort_text stuff is getting pretty confusing. I wonder if we can make a single function that sorts based on all the inputs (private module, length, etc) for autoimports, and a single function for completions (which calls the autoimports one for the import completions)? the logic being scattered across the file makes it difficult to understand

let is_private_import = handle_to_import_from
.module()
.components()
.last()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if non-last components should also be considered?

Copy link
Contributor

@kinto0 kinto0 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's what the referenced (#1685) issue is (_collections_abc.<>) so I'm not sure this PR fixes it without this change

None
},
sort_text: Some(format!("4{}", depth)),
sort_text: Some(format!("{name}:{private_rank}")),
Copy link
Contributor

@kinto0 kinto0 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is pretty confusing and isn't obvious based on lsp trace why we are sorting them. could it make sense to include metadata in the sort text?

e.g.: "private: {private_rank}"

} else {
None
},
sort_text: Some(format!("4{}", depth)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove the 4?

{
continue;
}
let depth = handle_to_import_from.module().components().len();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should also still consider depth. looks like CI didn't fail on this - would you be able to add a regression test that ensures shorter modules are preferred?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto import suggestions for collections.abc should take precedence over typing

4 participants